// Example for library:
// https://github.com/Bodmer/TJpg_Decoder

// This example is for an ESP32, it renders a Jpeg file that is stored in an array within Flash (program) memory
// see panda.h tab.  The panda image file being ~13Kbytes. The Jpeg decoding is done by one processor core 0 and
// the rendering to TFT by processor 1 (which is normally used to execute the Arduino sketches).

// Single processor core Flash_Jpg sketch on ILI9341 at 40MHz SPI renders the panda Jpeg in 103ms
// The Jpeg image takes 66ms to deocde and 37ms to render to TFT
// This sketch uses both ESP32 processors in parallel so decoding and rendering only takes 66ms
// Processor 1 runs the main sketch, processor 0 runs the Jpeg decoder

#include "WiFi.h"
//#include <WiFiManager.h>
#include <time.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>

#include "qing.h"
#include "yin.h"
#include "yun.h"
#include "yu.h"
#include "xue.h"
// Include the array
#include "panda.h"


// Include the jpeg decoder library
#include <TJpg_Decoder.h>
#include <PNGdec.h>

// Include the TFT library https://github.com/Bodmer/TFT_eSPI
#include "SPI.h"
#include <TFT_eSPI.h>               // Hardware-specific library
#include "Free_Fonts.h"
#include <U8g2_for_TFT_eSPI.h>

const char* ssid1 = "comicyu_2f";
const char* password1 = "67799706100";

#define NTP1 "ntp1.aliyun.com"
#define NTP2 "ntp2.aliyun.com"
#define NTP3 "ntp3.aliyun.com"

TFT_eSPI tft = TFT_eSPI();          // Invoke custom library
U8g2_for_TFT_eSPI  u8g2;

// Global variables available to BOTH processors 0 and 1
TaskHandle_t Task1;
const uint8_t* arrayName;           // Name of FLASH array containing Jpeg
bool doDecoding = false;            // Mutex flag to start decoding
bool mcuReady = false;              // Mutex flag to indicate an MCU block is ready for rendering
uint16_t mcuBuffer[16*16];          // Buffer to grab a snapshot of decoded MCU block
int32_t mcu_x, mcu_y, mcu_w, mcu_h; // Snapshot of the place to render the MCU

String wea_Temp="30";
String wea_TempRange="24-35";
String wea_TempH="35";
String wea_TempL="24";

String Weather_date="";
String Weather_desc_day="";
String Weather_desc_night="";
String wea_Desc="晴";
String wea_Wind="";
String Weather_reportdate="";

// This next function will be called by the TJpg_Decoder library during decoding of the jpeg file
// A copy of the decoded MCU block is grabbed for rendering so decoding can then continue while
// the MCU block is rendered on the TFT. Note: This function is called by processor 0
bool mcu_decoded(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
   // Stop further decoding as image is running off bottom of screen
  if ( y >= tft.height() ) return 0;

  while(mcuReady) yield(); // Wait here if rendering of last MCU block to TFT is still in progress

  memcpy(mcuBuffer, bitmap, 16*16*2); // Grab a copy of the MCU block image
  mcu_x = x;                          // Grab postion and size of MCU block
  mcu_y = y;
  mcu_w = w;
  mcu_h = h;
  mcuReady = true; // Flag to tell processor 1 that rendering of MCU can start

  // Return 1 to decode next Jpeg MCU block
  return 1;
}

// This is the task that runs on processor 0 (Arduino sketch runs on processor 1)
// It decodes the Jpeg image
void decodeJpg(void* p) {
  // This is an infinite loop, effectively the same as the normal sketch loop()
  // but this function and loop is running on processor 0
  for(;;) {
    // Decode the Jpeg image
    if (doDecoding) { // Only start decoding if main sketch sets this flag
      TJpgDec.drawJpg(0, 0, arrayName, sizeof(panda)); // Runs until complete image decoded
      doDecoding = false; // Set mutex false to indicate decoding has ended
    }
    // Must yield in this loop
    yield();
  }
}

void setup()
{
  //Create task decodeJpg to run on processor 0 to decode a Jpeg
  xTaskCreatePinnedToCore(decodeJpg, "decodeJpg", 10000, NULL, 0, NULL, 0);

  Serial.begin(115200);
  Serial.println("\n\n Testing TJpg_Decoder library");

  // Initialise the TFT
  tft.begin();
  u8g2.begin(tft);
  tft.setTextColor(0xFFFF, 0x0000);
  tft.fillScreen(TFT_BLACK);
  tft.setRotation(1);
  // The jpeg image can be scaled by a factor of 1, 2, 4, or 8
  TJpgDec.setJpgScale(1);
  // The byte order can be swapped (set true for TFT_eSPI)
  TJpgDec.setSwapBytes(true);
  // The decoder must be given the exact name of the mcu buffer function above
  TJpgDec.setCallback(mcu_decoded);

  connectWifi();

  configTime(8*3600,0,NTP1,NTP2,NTP3);
  struct tm timeinfo;
  while(!getLocalTime(&timeinfo, 3000))
  {
    delay(1000); 
    printBoth(".");   
  }
  Serial.println("Got time.");

  //tft.fillScreen(TFT_RED);
  showBackground();
  
  loop();
}

void connectWifi()
{
  int ic=0;
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  while (WiFi.status() != WL_CONNECTED)
  {
    printBoth("Connect to Wifi:");
    // printBoth(ssid);
    // ic=0;
    // WiFi.begin(ssid, password);
    // delay(1000);
    // while (WiFi.status() != WL_CONNECTED && ic<20) 
    // {  
    //   printBoth("-");
    //   delay(1000);
    //   ic++;
    // }
    
    if(WiFi.status() != WL_CONNECTED)
    {
      ic=0;

      printBoth("Change to Wifi:");
      printBoth(ssid1);

      WiFi.begin(ssid1, password1);
      delay(1000);
      while (WiFi.status() != WL_CONNECTED && ic<10) {  
        printBoth("-");
        delay(1000);
        ic++;
      }
    }
  }
    printBoth("WiFi connected!");
}

int ic=0;
String weekdays[7]={"日","一","二","三","四","五","六"};
void loop()
{
  // Wait before drawing again

  time_t now;
  now=time(NULL);
  struct tm * ltm=localtime(&now);

  showBackground();
  int y=108;

  if(ic%120==0)
  {
    getWeather();
    
  }
  ic=(ic+1)%3000;

  //hour
  tft.setCursor(45,y);
  tft.setFreeFont(FSS24);
  tft.setTextSize(2);
  tft.setTextColor(TFT_BLACK);
  tft.printf("%02d",ltm->tm_hour);
  tft.setCursor(168,y);
  tft.printf("%02d",ltm->tm_min);

//天气
  showWeatherImg();
  tft.setCursor(215,170);
  tft.setFreeFont(FSS18);
  tft.setTextSize(1);
  tft.setTextColor(TFT_WHITE);
  tft.print(wea_Temp);
  tft.setFreeFont(FSS9);
  tft.setCursor(260,155);
  tft.print(wea_TempH);
  tft.setCursor(260,170);
  tft.print(wea_TempL);
  
  u8g2.setCursor(40,160);
  u8g2.setFont(u8g2_font_wqy16_t_gb2312a);
  u8g2.setForegroundColor(TFT_WHITE);  //字的颜色
  u8g2.print(wea_Desc);
  char buffer[50];
  u8g2.setCursor(160,230);
  sprintf(buffer,"%d-%02d-%02d %s",ltm->tm_year+1900,ltm->tm_mon+1,ltm->tm_mday,weekdays[ltm->tm_wday]);
  u8g2.print(buffer);
  delay(1000*10);

}

void showBackground()
{
    // Get the width and height in pixels of the jpeg if you wish
  uint16_t w = 0, h = 0;
  TJpgDec.getJpgSize(&w, &h, panda, sizeof(panda));
  Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);

  // Time recorded for test purposes
  uint32_t t = millis();

  // The order here is important, doDecoding must be set "true" last after other parameters have been defined
  arrayName  = panda; // Name of FLASH array to be decoded
  mcuReady   = false; // Flag which is set true when a MCU block is ready for display
  doDecoding = true;  // Flag to tell task to decode the image

  // Only render MCU blocks if decoding is in progress OR an MCU is ready to render
  // Note: the OR mcuReady is required so the last block is rendered after decoding has ended
  while(doDecoding || mcuReady) {
    if (mcuReady) {
      tft.pushImage(mcu_x, mcu_y, mcu_w, mcu_h, mcuBuffer);
      mcuReady = false;
    }
    // Must yield in this loop
    yield();
  }

  // How much time did rendering take
  t = millis() - t;
  Serial.print(t); Serial.println(" ms");

}

void printBoth(const char* str)
{
  Serial.print(str);
  tft.print(str);
}

String getWeatherReq(String base)
{
  String result="";
  Serial.println("Getting weather ...");
  String url = "https://restapi.amap.com/v3/weather/weatherInfo?parameters&key=e094cccaa9f94c734344ab015d8b4ec6&city=110000&extensions="+base+"&output=JSON";
  HTTPClient http;
  http.begin(url);
  int httpCode=http.GET();
  if(httpCode>0)
    result = http.getString();
  Serial.println(result);
  return result;
}

void getWeather()
{
      String wea = getWeatherReq("base");
      if(wea.length()>0)
      {
        StaticJsonDocument<300> doc;
        deserializeJson(doc,wea);
      
        wea_Desc=String((const char *)doc["lives"][0]["weather"]);
        wea_Temp=String((const char *)doc["lives"][0]["temperature"]);
        wea_Wind=String((const char *)doc["lives"][0]["winddirection"])+String((const char *)doc["lives"][0]["windpower"]);
        Weather_reportdate=String((const char *)doc["lives"][0]["reporttime"]).substring(0,10);
      }
      
      if(Weather_date!=Weather_reportdate)
      {
        wea = getWeatherReq("all");
        if(wea.length()>0)
        {
          StaticJsonDocument<1500> doc;
          deserializeJson(doc,wea);
          Serial.println(wea);
          Weather_date=String((const char *)doc["forecasts"][0]["casts"][0]["date"]);
          Weather_desc_day=String((const char *)doc["forecasts"][0]["casts"][0]["dayweather"]);
          Weather_desc_night=String((const char *)doc["forecasts"][0]["casts"][0]["nightweather"]);
          wea_TempH=String((const char *)doc["forecasts"][0]["casts"][0]["daytemp"]);
          wea_TempL=String((const char *)doc["forecasts"][0]["casts"][0]["nighttemp"]);
        }
      }
}



#define SHOWPNG(img,x,y) showPng(((uint8_t*)img),sizeof(img),(x),(y))

void showWeatherImg()
{
    int weax=50;
    int weay=160;
    
    //enableDisp(0);
    if(wea_Desc.indexOf("晴")>=0)
      SHOWPNG(qing,weax,weay);
    else if (wea_Desc.indexOf("阴")>=0)
      SHOWPNG(yin,weax,weay);
    else if (wea_Desc.indexOf("雨")>=0)
      SHOWPNG(yu,weax,weay);
    else if (wea_Desc.indexOf("雪")>=0)
      SHOWPNG(xue,weax,weay);
    else
      SHOWPNG(yun,weax,weay);
}

//-------------------------------------------------
PNG png;
#define MAX_IMAGE_WIDTH 320
//=========================================v==========================================
//                                      pngDraw
//====================================================================================
// This next function will be called during decoding of the png file to
// render each image line to the TFT.  If you use a different TFT library
// you will need to adapt this function to suit.
// Callback function to draw pixels to the display

typedef struct my_private_struct
{
  int xoff, yoff; // corner offset
} PRIVATE;

void pngDraw(PNGDRAW *pDraw) {
  uint16_t lineBuffer[MAX_IMAGE_WIDTH];
  uint8_t  maskBuffer[1 + MAX_IMAGE_WIDTH / 8];  // Mask buffer
  png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff);
  PRIVATE *pPriv = (PRIVATE *)pDraw->pUser;
  if (png.getAlphaMask(pDraw, maskBuffer, 255)) {
    tft.pushMaskedImage(pPriv->xoff, pPriv->yoff + pDraw->y, pDraw->iWidth, 1, lineBuffer,maskBuffer);
  }
}


void showPng(uint8_t* img,int size,int x,int y)
{
  PRIVATE priv;
  int rc, i = 0;
  rc = png.openRAM(img,size, pngDraw);
  if (rc == PNG_SUCCESS) {
      Serial.printf("image specs: (%d x %d), %d bpp, pixel type: %d\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());
      priv.xoff = x;
      priv.yoff = y;
      rc = png.decode((void *)&priv, 0);
      png.close();
  }
}
